home *** CD-ROM | disk | FTP | other *** search
/ Clickx 115 / Clickx 115.iso / software / tools / windows / tails-i386-0.16.iso / live / filesystem.squashfs / usr / share / arm / cli / headerPanel.py < prev    next >
Encoding:
Python Source  |  2012-05-18  |  25.1 KB  |  601 lines

  1. """
  2. Top panel for every page, containing basic system and tor related information.
  3. If there's room available then this expands to present its information in two
  4. columns, otherwise it's laid out as follows:
  5.   arm - <hostname> (<os> <sys/version>)         Tor <tor/version> (<new, old, recommended, etc>)
  6.   <nickname> - <address>:<orPort>, [Dir Port: <dirPort>, ]Control Port (<open, password, cookie>): <controlPort>
  7.   cpu: <cpu%> mem: <mem> (<mem%>) uid: <uid> uptime: <upmin>:<upsec>
  8.   fingerprint: <fingerprint>
  9.  
  10. Example:
  11.   arm - odin (Linux 2.6.24-24-generic)         Tor 0.2.1.19 (recommended)
  12.   odin - 76.104.132.98:9001, Dir Port: 9030, Control Port (cookie): 9051
  13.   cpu: 14.6%    mem: 42 MB (4.2%)    pid: 20060   uptime: 48:27
  14.   fingerprint: BDAD31F6F318E0413833E8EBDA956F76E4D66788
  15. """
  16.  
  17. import os
  18. import time
  19. import curses
  20. import threading
  21.  
  22. import TorCtl.TorCtl
  23.  
  24. import starter
  25. import cli.popups
  26. import cli.controller
  27.  
  28. from util import log, panel, sysTools, torTools, uiTools
  29.  
  30. # minimum width for which panel attempts to double up contents (two columns to
  31. # better use screen real estate)
  32. MIN_DUAL_COL_WIDTH = 141
  33.  
  34. FLAG_COLORS = {"Authority": "white",  "BadExit": "red",     "BadDirectory": "red",    "Exit": "cyan",
  35.                "Fast": "yellow",      "Guard": "green",     "HSDir": "magenta",       "Named": "blue",
  36.                "Stable": "blue",      "Running": "yellow",  "Unnamed": "magenta",     "Valid": "green",
  37.                "V2Dir": "cyan",       "V3Dir": "white"}
  38.  
  39. VERSION_STATUS_COLORS = {"new": "blue", "new in series": "blue", "obsolete": "red", "recommended": "green",  
  40.                          "old": "red",  "unrecommended": "red",  "unknown": "cyan"}
  41.  
  42. DEFAULT_CONFIG = {"startup.interface.ipAddress": "127.0.0.1",
  43.                   "startup.interface.port": 9051,
  44.                   "startup.interface.socket": "/var/run/tor/control",
  45.                   "features.showFdUsage": False,
  46.                   "log.fdUsageSixtyPercent": log.NOTICE,
  47.                   "log.fdUsageNinetyPercent": log.WARN}
  48.  
  49. class HeaderPanel(panel.Panel, threading.Thread):
  50.   """
  51.   Top area contenting tor settings and system information. Stats are stored in
  52.   the vals mapping, keys including:
  53.     tor/  version, versionStatus, nickname, orPort, dirPort, controlPort,
  54.           socketPath, exitPolicy, isAuthPassword (bool), isAuthCookie (bool),
  55.           orListenAddr, *address, *fingerprint, *flags, pid, startTime,
  56.           *fdUsed, fdLimit, isFdLimitEstimate
  57.     sys/  hostname, os, version
  58.     stat/ *%torCpu, *%armCpu, *rss, *%mem
  59.   
  60.   * volatile parameter that'll be reset on each update
  61.   """
  62.   
  63.   def __init__(self, stdscr, startTime, config = None):
  64.     panel.Panel.__init__(self, stdscr, "header", 0)
  65.     threading.Thread.__init__(self)
  66.     self.setDaemon(True)
  67.     
  68.     self._config = dict(DEFAULT_CONFIG)
  69.     if config: config.update(self._config)
  70.     
  71.     self._isTorConnected = torTools.getConn().isAlive()
  72.     self._lastUpdate = -1       # time the content was last revised
  73.     self._halt = False          # terminates thread if true
  74.     self._cond = threading.Condition()  # used for pausing the thread
  75.     
  76.     # Time when the panel was paused or tor was stopped. This is used to
  77.     # freeze the uptime statistic (uptime increments normally when None).
  78.     self._haltTime = None
  79.     
  80.     # The last arm cpu usage sampling taken. This is a tuple of the form:
  81.     # (total arm cpu time, sampling timestamp)
  82.     # 
  83.     # The initial cpu total should be zero. However, at startup the cpu time
  84.     # in practice is often greater than the real time causing the initially
  85.     # reported cpu usage to be over 100% (which shouldn't be possible on
  86.     # single core systems).
  87.     # 
  88.     # Setting the initial cpu total to the value at this panel's init tends to
  89.     # give smoother results (staying in the same ballpark as the second
  90.     # sampling) so fudging the numbers this way for now.
  91.     
  92.     self._armCpuSampling = (sum(os.times()[:3]), startTime)
  93.     
  94.     # Last sampling received from the ResourceTracker, used to detect when it
  95.     # changes.
  96.     self._lastResourceFetch = -1
  97.     
  98.     # flag to indicate if we've already given file descriptor warnings
  99.     self._isFdSixtyPercentWarned = False
  100.     self._isFdNinetyPercentWarned = False
  101.     
  102.     self.vals = {}
  103.     self.valsLock = threading.RLock()
  104.     self._update(True)
  105.     
  106.     # listens for tor reload (sighup) events
  107.     torTools.getConn().addStatusListener(self.resetListener)
  108.   
  109.   def getHeight(self):
  110.     """
  111.     Provides the height of the content, which is dynamically determined by the
  112.     panel's maximum width.
  113.     """
  114.     
  115.     isWide = self.getParent().getmaxyx()[1] >= MIN_DUAL_COL_WIDTH
  116.     if self.vals["tor/orPort"]: return 4 if isWide else 6
  117.     else: return 3 if isWide else 4
  118.   
  119.   def sendNewnym(self):
  120.     """
  121.     Requests a new identity and provides a visual queue.
  122.     """
  123.     
  124.     torTools.getConn().sendNewnym()
  125.     
  126.     # If we're wide then the newnym label in this panel will give an
  127.     # indication that the signal was sent. Otherwise use a msg.
  128.     isWide = self.getParent().getmaxyx()[1] >= MIN_DUAL_COL_WIDTH
  129.     if not isWide: cli.popups.showMsg("Requesting a new identity", 1)
  130.   
  131.   def handleKey(self, key):
  132.     isKeystrokeConsumed = True
  133.     
  134.     if key in (ord('n'), ord('N')) and torTools.getConn().isNewnymAvailable():
  135.       self.sendNewnym()
  136.     elif key in (ord('r'), ord('R')) and not self._isTorConnected:
  137.       torctlConn = None
  138.       allowPortConnection, allowSocketConnection, _ = starter.allowConnectionTypes()
  139.       
  140.       if os.path.exists(self._config["startup.interface.socket"]) and allowSocketConnection:
  141.         try: torctlConn = torTools.connect_socket(self._config["startup.interface.socket"])
  142.         except IOError, exc:
  143.           if not allowPortConnection:
  144.             cli.popups.showMsg("Unable to reconnect (%s)" % exc, 3)
  145.       elif not allowPortConnection:
  146.         cli.popups.showMsg("Unable to reconnect (socket '%s' doesn't exist)" % self._config["startup.interface.socket"], 3)
  147.       
  148.       if not torctlConn and allowPortConnection:
  149.         # TODO: This has diverged from starter.py's connection, for instance it
  150.         # doesn't account for relative cookie paths or multiple authentication
  151.         # methods. We can't use the starter.py's connection function directly
  152.         # due to password prompts, but we could certainly make this mess more
  153.         # manageable.
  154.         
  155.         try:
  156.           ctlAddr, ctlPort = self._config["startup.interface.ipAddress"], self._config["startup.interface.port"]
  157.           tmpConn, authType, authValue = TorCtl.TorCtl.preauth_connect(ctlAddr, ctlPort)
  158.           
  159.           if authType == TorCtl.TorCtl.AUTH_TYPE.PASSWORD:
  160.             authValue = cli.popups.inputPrompt("Controller Password: ")
  161.             if not authValue: raise IOError() # cancel reconnection
  162.           elif authType == TorCtl.TorCtl.AUTH_TYPE.COOKIE:
  163.             authCookieSize = os.path.getsize(authValue)
  164.             if authCookieSize != 32:
  165.               raise IOError("authentication cookie '%s' is the wrong size (%i bytes instead of 32)" % (authValue, authCookieSize))
  166.           
  167.           tmpConn.authenticate(authValue)
  168.           torctlConn = tmpConn
  169.         except Exception, exc:
  170.           # attempts to use the wizard port too
  171.           try:
  172.             cli.controller.getController().getTorManager().connectManagedInstance()
  173.             log.log(log.NOTICE, "Reconnected to Tor's control port")
  174.             cli.popups.showMsg("Tor reconnected", 1)
  175.           except:
  176.             # displays notice for the first failed connection attempt
  177.             if exc.args: cli.popups.showMsg("Unable to reconnect (%s)" % exc, 3)
  178.       
  179.       if torctlConn:
  180.         torTools.getConn().init(torctlConn)
  181.         log.log(log.NOTICE, "Reconnected to Tor's control port")
  182.         cli.popups.showMsg("Tor reconnected", 1)
  183.     else: isKeystrokeConsumed = False
  184.     
  185.     return isKeystrokeConsumed
  186.   
  187.   def draw(self, width, height):
  188.     self.valsLock.acquire()
  189.     isWide = width + 1 >= MIN_DUAL_COL_WIDTH
  190.     
  191.     # space available for content
  192.     if isWide:
  193.       leftWidth = max(width / 2, 77)
  194.       rightWidth = width - leftWidth
  195.     else: leftWidth = rightWidth = width
  196.     
  197.     # Line 1 / Line 1 Left (system and tor version information)
  198.     sysNameLabel = "arm - %s" % self.vals["sys/hostname"]
  199.     contentSpace = min(leftWidth, 40)
  200.     
  201.     if len(sysNameLabel) + 10 <= contentSpace:
  202.       sysTypeLabel = "%s %s" % (self.vals["sys/os"], self.vals["sys/version"])
  203.       sysTypeLabel = uiTools.cropStr(sysTypeLabel, contentSpace - len(sysNameLabel) - 3, 4)
  204.       self.addstr(0, 0, "%s (%s)" % (sysNameLabel, sysTypeLabel))
  205.     else:
  206.       self.addstr(0, 0, uiTools.cropStr(sysNameLabel, contentSpace))
  207.     
  208.     contentSpace = leftWidth - 43
  209.     if 7 + len(self.vals["tor/version"]) + len(self.vals["tor/versionStatus"]) <= contentSpace:
  210.       if self.vals["tor/version"] != "Unknown":
  211.         versionColor = VERSION_STATUS_COLORS[self.vals["tor/versionStatus"]] if \
  212.             self.vals["tor/versionStatus"] in VERSION_STATUS_COLORS else "white"
  213.         labelPrefix = "Tor %s (" % self.vals["tor/version"]
  214.         self.addstr(0, 43, labelPrefix)
  215.         self.addstr(0, 43 + len(labelPrefix), self.vals["tor/versionStatus"], uiTools.getColor(versionColor))
  216.         self.addstr(0, 43 + len(labelPrefix) + len(self.vals["tor/versionStatus"]), ")")
  217.     elif 11 <= contentSpace:
  218.       self.addstr(0, 43, uiTools.cropStr("Tor %s" % self.vals["tor/version"], contentSpace, 4))
  219.     
  220.     # Line 2 / Line 2 Left (tor ip/port information)
  221.     x, includeControlPort = 0, True
  222.     if self.vals["tor/orPort"]:
  223.       myAddress = "Unknown"
  224.       if self.vals["tor/orListenAddr"]: myAddress = self.vals["tor/orListenAddr"]
  225.       elif self.vals["tor/address"]: myAddress = self.vals["tor/address"]
  226.       
  227.       # acting as a relay (we can assume certain parameters are set
  228.       dirPortLabel = ", Dir Port: %s" % self.vals["tor/dirPort"] if self.vals["tor/dirPort"] != "0" else ""
  229.       for label in (self.vals["tor/nickname"], " - " + myAddress, ":" + self.vals["tor/orPort"], dirPortLabel):
  230.         if x + len(label) <= leftWidth:
  231.           self.addstr(1, x, label)
  232.           x += len(label)
  233.         else: break
  234.     else:
  235.       # non-relay (client only)
  236.       if self._isTorConnected:
  237.         self.addstr(1, x, "Relaying Disabled", uiTools.getColor("cyan"))
  238.         x += 17
  239.       else:
  240.         statusTime = torTools.getConn().getStatus()[1]
  241.         
  242.         if statusTime:
  243.           statusTimeLabel = time.strftime("%H:%M %m/%d/%Y, ", time.localtime(statusTime))
  244.         else: statusTimeLabel = "" # never connected to tor
  245.         
  246.         self.addstr(1, x, "Tor Disconnected", curses.A_BOLD | uiTools.getColor("red"))
  247.         self.addstr(1, x + 16, " (%spress r to reconnect)" % statusTimeLabel)
  248.         x += 39 + len(statusTimeLabel)
  249.         includeControlPort = False
  250.     
  251.     if includeControlPort:
  252.       if self.vals["tor/controlPort"] == "0":
  253.         # connected via a control socket
  254.         self.addstr(1, x, ", Control Socket: %s" % self.vals["tor/socketPath"])
  255.       else:
  256.         if self.vals["tor/isAuthPassword"]: authType = "password"
  257.         elif self.vals["tor/isAuthCookie"]: authType = "cookie"
  258.         else: authType = "open"
  259.         
  260.         if x + 19 + len(self.vals["tor/controlPort"]) + len(authType) <= leftWidth:
  261.           authColor = "red" if authType == "open" else "green"
  262.           self.addstr(1, x, ", Control Port (")
  263.           self.addstr(1, x + 16, authType, uiTools.getColor(authColor))
  264.           self.addstr(1, x + 16 + len(authType), "): %s" % self.vals["tor/controlPort"])
  265.         elif x + 16 + len(self.vals["tor/controlPort"]) <= leftWidth:
  266.           self.addstr(1, 0, ", Control Port: %s" % self.vals["tor/controlPort"])
  267.     
  268.     # Line 3 / Line 1 Right (system usage info)
  269.     y, x = (0, leftWidth) if isWide else (2, 0)
  270.     if self.vals["stat/rss"] != "0": memoryLabel = uiTools.getSizeLabel(int(self.vals["stat/rss"]))
  271.     else: memoryLabel = "0"
  272.     
  273.     uptimeLabel = ""
  274.     if self.vals["tor/startTime"]:
  275.       if self.isPaused() or not self._isTorConnected:
  276.         # freeze the uptime when paused or the tor process is stopped
  277.         uptimeLabel = uiTools.getShortTimeLabel(self.getPauseTime() - self.vals["tor/startTime"])
  278.       else:
  279.         uptimeLabel = uiTools.getShortTimeLabel(time.time() - self.vals["tor/startTime"])
  280.     
  281.     sysFields = ((0, "cpu: %s%% tor, %s%% arm" % (self.vals["stat/%torCpu"], self.vals["stat/%armCpu"])),
  282.                  (27, "mem: %s (%s%%)" % (memoryLabel, self.vals["stat/%mem"])),
  283.                  (47, "pid: %s" % (self.vals["tor/pid"] if self._isTorConnected else "")),
  284.                  (59, "uptime: %s" % uptimeLabel))
  285.     
  286.     for (start, label) in sysFields:
  287.       if start + len(label) <= rightWidth: self.addstr(y, x + start, label)
  288.       else: break
  289.     
  290.     if self.vals["tor/orPort"]:
  291.       # Line 4 / Line 2 Right (fingerprint, and possibly file descriptor usage)
  292.       y, x = (1, leftWidth) if isWide else (3, 0)
  293.       
  294.       fingerprintLabel = uiTools.cropStr("fingerprint: %s" % self.vals["tor/fingerprint"], width)
  295.       self.addstr(y, x, fingerprintLabel)
  296.       
  297.       # if there's room and we're able to retrieve both the file descriptor
  298.       # usage and limit then it might be presented
  299.       if width - x - 59 >= 20 and self.vals["tor/fdUsed"] and self.vals["tor/fdLimit"]:
  300.         # display file descriptor usage if we're either configured to do so or
  301.         # running out
  302.         
  303.         fdPercent = 100 * self.vals["tor/fdUsed"] / self.vals["tor/fdLimit"]
  304.         
  305.         if fdPercent >= 60 or self._config["features.showFdUsage"]:
  306.           fdPercentLabel, fdPercentFormat = "%i%%" % fdPercent, curses.A_NORMAL
  307.           if fdPercent >= 95:
  308.             fdPercentFormat = curses.A_BOLD | uiTools.getColor("red")
  309.           elif fdPercent >= 90:
  310.             fdPercentFormat = uiTools.getColor("red")
  311.           elif fdPercent >= 60:
  312.             fdPercentFormat = uiTools.getColor("yellow")
  313.           
  314.           estimateChar = "?" if self.vals["tor/isFdLimitEstimate"] else ""
  315.           baseLabel = "file desc: %i / %i%s (" % (self.vals["tor/fdUsed"], self.vals["tor/fdLimit"], estimateChar)
  316.           
  317.           self.addstr(y, x + 59, baseLabel)
  318.           self.addstr(y, x + 59 + len(baseLabel), fdPercentLabel, fdPercentFormat)
  319.           self.addstr(y, x + 59 + len(baseLabel) + len(fdPercentLabel), ")")
  320.       
  321.       # Line 5 / Line 3 Left (flags)
  322.       if self._isTorConnected:
  323.         y, x = (2 if isWide else 4, 0)
  324.         self.addstr(y, x, "flags: ")
  325.         x += 7
  326.         
  327.         if len(self.vals["tor/flags"]) > 0:
  328.           for i in range(len(self.vals["tor/flags"])):
  329.             flag = self.vals["tor/flags"][i]
  330.             flagColor = FLAG_COLORS[flag] if flag in FLAG_COLORS.keys() else "white"
  331.             
  332.             self.addstr(y, x, flag, curses.A_BOLD | uiTools.getColor(flagColor))
  333.             x += len(flag)
  334.             
  335.             if i < len(self.vals["tor/flags"]) - 1:
  336.               self.addstr(y, x, ", ")
  337.               x += 2
  338.         else:
  339.           self.addstr(y, x, "none", curses.A_BOLD | uiTools.getColor("cyan"))
  340.       else:
  341.         y = 2 if isWide else 4
  342.         statusTime = torTools.getConn().getStatus()[1]
  343.         statusTimeLabel = time.strftime("%H:%M %m/%d/%Y", time.localtime(statusTime))
  344.         self.addstr(y, 0, "Tor Disconnected", curses.A_BOLD | uiTools.getColor("red"))
  345.         self.addstr(y, 16, " (%s) - press r to reconnect" % statusTimeLabel)
  346.       
  347.       # Undisplayed / Line 3 Right (exit policy)
  348.       if isWide:
  349.         exitPolicy = self.vals["tor/exitPolicy"]
  350.         
  351.         # adds note when default exit policy is appended
  352.         if exitPolicy == "": exitPolicy = "<default>"
  353.         elif not exitPolicy.endswith((" *:*", " *")): exitPolicy += ", <default>"
  354.         
  355.         self.addstr(2, leftWidth, "exit policy: ")
  356.         x = leftWidth + 13
  357.         
  358.         # color codes accepts to be green, rejects to be red, and default marker to be cyan
  359.         isSimple = len(exitPolicy) > rightWidth - 13
  360.         policies = exitPolicy.split(", ")
  361.         for i in range(len(policies)):
  362.           policy = policies[i].strip()
  363.           policyLabel = policy.replace("accept", "").replace("reject", "").strip() if isSimple else policy
  364.           
  365.           policyColor = "white"
  366.           if policy.startswith("accept"): policyColor = "green"
  367.           elif policy.startswith("reject"): policyColor = "red"
  368.           elif policy.startswith("<default>"): policyColor = "cyan"
  369.           
  370.           self.addstr(2, x, policyLabel, curses.A_BOLD | uiTools.getColor(policyColor))
  371.           x += len(policyLabel)
  372.           
  373.           if i < len(policies) - 1:
  374.             self.addstr(2, x, ", ")
  375.             x += 2
  376.     else:
  377.       # (Client only) Undisplayed / Line 2 Right (new identity option)
  378.       if isWide:
  379.         conn = torTools.getConn()
  380.         newnymWait = conn.getNewnymWait()
  381.         
  382.         msg = "press 'n' for a new identity"
  383.         if newnymWait > 0:
  384.           pluralLabel = "s" if newnymWait > 1 else ""
  385.           msg = "building circuits, available again in %i second%s" % (newnymWait, pluralLabel)
  386.         
  387.         self.addstr(1, leftWidth, msg)
  388.     
  389.     self.valsLock.release()
  390.   
  391.   def getPauseTime(self):
  392.     """
  393.     Provides the time Tor stopped if it isn't running. Otherwise this is the
  394.     time we were last paused.
  395.     """
  396.     
  397.     if self._haltTime: return self._haltTime
  398.     else: return panel.Panel.getPauseTime(self)
  399.   
  400.   def run(self):
  401.     """
  402.     Keeps stats updated, checking for new information at a set rate.
  403.     """
  404.     
  405.     lastDraw = time.time() - 1
  406.     while not self._halt:
  407.       currentTime = time.time()
  408.       
  409.       if self.isPaused() or currentTime - lastDraw < 1 or not self._isTorConnected:
  410.         self._cond.acquire()
  411.         if not self._halt: self._cond.wait(0.2)
  412.         self._cond.release()
  413.       else:
  414.         # Update the volatile attributes (cpu, memory, flags, etc) if we have
  415.         # a new resource usage sampling (the most dynamic stat) or its been
  416.         # twenty seconds since last fetched (so we still refresh occasionally
  417.         # when resource fetches fail).
  418.         # 
  419.         # Otherwise, just redraw the panel to change the uptime field.
  420.         
  421.         isChanged = False
  422.         if self.vals["tor/pid"]:
  423.           resourceTracker = sysTools.getResourceTracker(self.vals["tor/pid"])
  424.           isChanged = self._lastResourceFetch != resourceTracker.getRunCount()
  425.         
  426.         if isChanged or currentTime - self._lastUpdate >= 20:
  427.           self._update()
  428.         
  429.         self.redraw(True)
  430.         lastDraw += 1
  431.   
  432.   def stop(self):
  433.     """
  434.     Halts further resolutions and terminates the thread.
  435.     """
  436.     
  437.     self._cond.acquire()
  438.     self._halt = True
  439.     self._cond.notifyAll()
  440.     self._cond.release()
  441.   
  442.   def resetListener(self, _, eventType):
  443.     """
  444.     Updates static parameters on tor reload (sighup) events.
  445.     
  446.     Arguments:
  447.       conn      - tor controller
  448.       eventType - type of event detected
  449.     """
  450.     
  451.     if eventType in (torTools.State.INIT, torTools.State.RESET):
  452.       initialHeight = self.getHeight()
  453.       self._isTorConnected = True
  454.       self._haltTime = None
  455.       self._update(True)
  456.       
  457.       if self.getHeight() != initialHeight:
  458.         # We're toggling between being a relay and client, causing the height
  459.         # of this panel to change. Redraw all content so we don't get
  460.         # overlapping content.
  461.         cli.controller.getController().redraw()
  462.       else:
  463.         # just need to redraw ourselves
  464.         self.redraw(True)
  465.     elif eventType == torTools.State.CLOSED:
  466.       self._isTorConnected = False
  467.       self._haltTime = time.time()
  468.       self._update()
  469.       self.redraw(True)
  470.   
  471.   def _update(self, setStatic=False):
  472.     """
  473.     Updates stats in the vals mapping. By default this just revises volatile
  474.     attributes.
  475.     
  476.     Arguments:
  477.       setStatic - resets all parameters, including relatively static values
  478.     """
  479.     
  480.     self.valsLock.acquire()
  481.     conn = torTools.getConn()
  482.     
  483.     if setStatic:
  484.       # version is truncated to first part, for instance:
  485.       # 0.2.2.13-alpha (git-feb8c1b5f67f2c6f) -> 0.2.2.13-alpha
  486.       self.vals["tor/version"] = conn.getInfo("version", "Unknown").split()[0]
  487.       self.vals["tor/versionStatus"] = conn.getInfo("status/version/current", "Unknown")
  488.       self.vals["tor/nickname"] = conn.getOption("Nickname", "")
  489.       self.vals["tor/orPort"] = conn.getOption("ORPort", "0")
  490.       self.vals["tor/dirPort"] = conn.getOption("DirPort", "0")
  491.       self.vals["tor/controlPort"] = conn.getOption("ControlPort", "0")
  492.       self.vals["tor/socketPath"] = conn.getOption("ControlSocket", "")
  493.       self.vals["tor/isAuthPassword"] = conn.getOption("HashedControlPassword") != None
  494.       self.vals["tor/isAuthCookie"] = conn.getOption("CookieAuthentication") == "1"
  495.       
  496.       # orport is reported as zero if unset
  497.       if self.vals["tor/orPort"] == "0": self.vals["tor/orPort"] = ""
  498.       
  499.       # overwrite address if ORListenAddress is set (and possibly orPort too)
  500.       self.vals["tor/orListenAddr"] = ""
  501.       listenAddr = conn.getOption("ORListenAddress")
  502.       if listenAddr:
  503.         if ":" in listenAddr:
  504.           # both ip and port overwritten
  505.           self.vals["tor/orListenAddr"] = listenAddr[:listenAddr.find(":")]
  506.           self.vals["tor/orPort"] = listenAddr[listenAddr.find(":") + 1:]
  507.         else:
  508.           self.vals["tor/orListenAddr"] = listenAddr
  509.       
  510.       # fetch exit policy (might span over multiple lines)
  511.       policyEntries = []
  512.       for exitPolicy in conn.getOption("ExitPolicy", [], True):
  513.         policyEntries += [policy.strip() for policy in exitPolicy.split(",")]
  514.       self.vals["tor/exitPolicy"] = ", ".join(policyEntries)
  515.       
  516.       # file descriptor limit for the process, if this can't be determined
  517.       # then the limit is None
  518.       fdLimit, fdIsEstimate = conn.getMyFileDescriptorLimit()
  519.       self.vals["tor/fdLimit"] = fdLimit
  520.       self.vals["tor/isFdLimitEstimate"] = fdIsEstimate
  521.       
  522.       # system information
  523.       unameVals = os.uname()
  524.       self.vals["sys/hostname"] = unameVals[1]
  525.       self.vals["sys/os"] = unameVals[0]
  526.       self.vals["sys/version"] = unameVals[2]
  527.       
  528.       pid = conn.getMyPid()
  529.       self.vals["tor/pid"] = pid if pid else ""
  530.       
  531.       startTime = conn.getStartTime()
  532.       self.vals["tor/startTime"] = startTime if startTime else ""
  533.       
  534.       # reverts volatile parameters to defaults
  535.       self.vals["tor/fingerprint"] = "Unknown"
  536.       self.vals["tor/flags"] = []
  537.       self.vals["tor/fdUsed"] = 0
  538.       self.vals["stat/%torCpu"] = "0"
  539.       self.vals["stat/%armCpu"] = "0"
  540.       self.vals["stat/rss"] = "0"
  541.       self.vals["stat/%mem"] = "0"
  542.     
  543.     # sets volatile parameters
  544.     # TODO: This can change, being reported by STATUS_SERVER -> EXTERNAL_ADDRESS
  545.     # events. Introduce caching via torTools?
  546.     self.vals["tor/address"] = conn.getInfo("address", "")
  547.     
  548.     self.vals["tor/fingerprint"] = conn.getInfo("fingerprint", self.vals["tor/fingerprint"])
  549.     self.vals["tor/flags"] = conn.getMyFlags(self.vals["tor/flags"])
  550.     
  551.     # Updates file descriptor usage and logs if the usage is high. If we don't
  552.     # have a known limit or it's obviously faulty (being lower than our
  553.     # current usage) then omit file descriptor functionality.
  554.     if self.vals["tor/fdLimit"]:
  555.       fdUsed = conn.getMyFileDescriptorUsage()
  556.       if fdUsed and fdUsed <= self.vals["tor/fdLimit"]: self.vals["tor/fdUsed"] = fdUsed
  557.       else: self.vals["tor/fdUsed"] = 0
  558.     
  559.     if self.vals["tor/fdUsed"] and self.vals["tor/fdLimit"]:
  560.       fdPercent = 100 * self.vals["tor/fdUsed"] / self.vals["tor/fdLimit"]
  561.       estimatedLabel = " estimated" if self.vals["tor/isFdLimitEstimate"] else ""
  562.       msg = "Tor's%s file descriptor usage is at %i%%." % (estimatedLabel, fdPercent)
  563.       
  564.       if fdPercent >= 90 and not self._isFdNinetyPercentWarned:
  565.         self._isFdSixtyPercentWarned, self._isFdNinetyPercentWarned = True, True
  566.         msg += " If you run out Tor will be unable to continue functioning."
  567.         log.log(self._config["log.fdUsageNinetyPercent"], msg)
  568.       elif fdPercent >= 60 and not self._isFdSixtyPercentWarned:
  569.         self._isFdSixtyPercentWarned = True
  570.         log.log(self._config["log.fdUsageSixtyPercent"], msg)
  571.     
  572.     # ps or proc derived resource usage stats
  573.     if self.vals["tor/pid"]:
  574.       resourceTracker = sysTools.getResourceTracker(self.vals["tor/pid"])
  575.       
  576.       if resourceTracker.lastQueryFailed():
  577.         self.vals["stat/%torCpu"] = "0"
  578.         self.vals["stat/rss"] = "0"
  579.         self.vals["stat/%mem"] = "0"
  580.       else:
  581.         cpuUsage, _, memUsage, memUsagePercent = resourceTracker.getResourceUsage()
  582.         self._lastResourceFetch = resourceTracker.getRunCount()
  583.         self.vals["stat/%torCpu"] = "%0.1f" % (100 * cpuUsage)
  584.         self.vals["stat/rss"] = str(memUsage)
  585.         self.vals["stat/%mem"] = "%0.1f" % (100 * memUsagePercent)
  586.     
  587.     # determines the cpu time for the arm process (including user and system
  588.     # time of both the primary and child processes)
  589.     
  590.     totalArmCpuTime, currentTime = sum(os.times()[:3]), time.time()
  591.     armCpuDelta = totalArmCpuTime - self._armCpuSampling[0]
  592.     armTimeDelta = currentTime - self._armCpuSampling[1]
  593.     pythonCpuTime = armCpuDelta / armTimeDelta
  594.     sysCallCpuTime = sysTools.getSysCpuUsage()
  595.     self.vals["stat/%armCpu"] = "%0.1f" % (100 * (pythonCpuTime + sysCallCpuTime))
  596.     self._armCpuSampling = (totalArmCpuTime, currentTime)
  597.     
  598.     self._lastUpdate = currentTime
  599.     self.valsLock.release()
  600.  
  601.